﻿using JBeamLibrary.Exceptions;
using JBeamLibrary.Tools;
using JBeamLibrary.VehicleParts.Elements;
using JBeamLibrary.VehicleParts.Enums;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JBeamLibrary.VehicleParts.Groups;
using JBeamLibrary.VehicleParts.Interfaces;
using Newtonsoft.Json.Linq;

namespace JBeamLibrary.VehicleParts.Collections
{
    public class VehicleSectionProperties<T> : KeyedCollection<int, ElementPropertyCollection> where T : class, IVehicleElement
    {
        #region Properties

        public VehicleSection<T> Section { get; private set; } 

        #endregion

        #region Indexers

        /// <summary>Gets the property collection for the specified element index</summary>
        /// <param name="elementIndex"></param>
        /// <returns></returns>
        public new ElementPropertyCollection this[int elementIndex]
        {
            get
            {
                return !Contains(elementIndex) ? null : base[elementIndex];
            }
        }

        #endregion

        #region CTORs

        public VehicleSectionProperties(VehicleSection<T> section)
        {
            Section = section;
        }

        #endregion

        #region Methods

        private new void Add(ElementPropertyCollection properties)
        {
            base.Add(properties);
        }

        private new void Remove(ElementPropertyCollection properties)
        {
            base.Remove(properties);
        }

        private new void Remove(int key)
        {
            base.Remove(key);
        }

        public new void RemoveAt(int index)
        {
            var properties = this[index];
            if (properties == null) return;

            properties.Clear();
            base.RemoveAt(index);
        }

        public void SetPropertyValue(int elementIndex, JProperty prop, ElementPropertyUse use)
        {
            var val = ElementProperty.FromJProperty(prop, use);
            SetPropertyValue(elementIndex, val.Property, use, val.Value);
        }

        public void SetPropertyValue(int elementIndex, ElementProperties property, ElementPropertyUse use, object value)
        {
            if (!Contains(elementIndex))
                base.Add(new ElementPropertyCollection(elementIndex));
            var properties = base[elementIndex];

            /* Get current existing property */
            var current = GetProperty(elementIndex, property, ElementPropertyUse.Group) ??
                          GetProperty(elementIndex, property, ElementPropertyUse.Single);

            /* Delete existing one */
            if (current != null && value.SafeEquals(ElementPropertyTypes.NullValues[ElementPropertyTypes.TypeMap[property]]))
            {
                properties.Remove(property);
                if (properties.Count == 0) Remove(elementIndex);
            }

            /* Update existing one */
            else if (current != null)
            {
                current.Value = value;
                current.Use = use;
            }

            /* Define new property */
            else
            {
                try
                {
                    properties.Add(new ElementProperty(property, use, value));
                }
                catch (ArgumentException ex)
                {
                    throw new InvalidPropertyException<T>(Section, property, value, use, ex);
                }
            }
        }

        public void SetPropertyValue(int elementIndex, string propertyName, ElementPropertyUse use, object value)
        {
            var prop = GroupHelper.GetPropertyFromName(propertyName);
            SetPropertyValue(elementIndex, prop, use, value);
        }

        public ElementProperty GetProperty(int elementIndex, ElementProperties property, ElementPropertyUse use)
        {
            var properties = base[elementIndex];
            if (properties == null) return null;

            return properties.FirstOrDefault(prop => prop.Property == property && prop.Use == use);
        }

        public ElementProperty GetProperty(int elementIndex, string propertyName, ElementPropertyUse use)
        {
            var prop = GroupHelper.GetPropertyFromName(propertyName);
            return GetProperty(elementIndex, prop, use);
        }

        public object GetPropertyValue(int elementIndex, ElementProperties property, ElementPropertyUse use)
        {
            var properties = this[elementIndex];
            if (properties == null) return null;

            var elementProperty = properties.FirstOrDefault(prop => prop.Property == property && prop.Use == use);
            return elementProperty != null ? elementProperty.Value : null;
        }

        public object GetPropertyValue(int elementIndex, string propertyName, ElementPropertyUse use)
        {
            var prop = GroupHelper.GetPropertyFromName(propertyName);
            return GetPropertyValue(elementIndex, prop, use);
        }

        public object GetLastPropertyValue(ElementProperties property, ElementPropertyUse use)
        {
            var index = Count - 1;
            while (index >= 0)
            {
                var val = GetPropertyValue(index, property, use);
                if (val != null) return val;
                index--;
            }
            return null;
        }

        public object GetLastPropertyValue(string propertyName, ElementPropertyUse use)
        {
            var prop = GroupHelper.GetPropertyFromName(propertyName);
            return GetLastPropertyValue(prop, use);
        }

        #endregion

        #region KeyedCollection

        protected override int GetKeyForItem(ElementPropertyCollection item)
        {
            return item.ElementIndex;
        }

        #endregion
    }
}
